﻿using IntoCommon.Util;
using S7.Net;
using S7.Net.Types;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using IntoStationRepair.Configs;

namespace IntoStationRepair.PLC
{
    public class Service
    {
        private Plc plc;
        CommunicationModel communicae = new CommunicationModel();
        Thread thread;
        bool IsConnected = false;
        private int db, len, offset, mesDb, mesLen, mesOffset;
        private DataType type = DataType.DataBlock;
        private byte[] datas, mesData;


        private string repairConfigFile = Path.Combine(@"D:\", "datas", "RepairConfig.xml");
        public RepairConfig config = new RepairConfig();

        public delegate void StatusChangedHandle(CommunicationModel data);
        public StatusChangedHandle StatusChangedEvent;
        private void OnStatusChanged()
        {
            if (StatusChangedEvent != null)
            {
                StatusChangedEvent.Invoke(communicae);
            }
        }


        public Service()
        {
            LoadConfig();
            Init();
        }

        private void LoadConfig()
        {
            if (File.Exists(repairConfigFile))
            {
                config = GlobalFunc.LoadXmlFile<RepairConfig>(repairConfigFile);
            }
            else
            {
                config = new RepairConfig();
                GlobalFunc.WriteXmlFile(repairConfigFile, config);
            }
        }
        private void Init()
        {
            plc = new Plc(CpuType.S71200, config.PlcIP, 0, 1);
            db = config.DbBlock;
            len = config.Len;
            offset = config.Offset;
            mesDb = config.MesDb;
            mesLen = config.MesLen;
            mesOffset = config.MesOffset;
            datas = new byte[len];
            mesData = new byte[mesLen];
            for (int i = 0; i < communicae.Devs.Length; i++)
            {
                communicae.Devs[i] = new DevData();
            }
            try
            {
                plc.Open();
                if (plc.IsConnected)
                {
                    thread = new Thread(Communicate) { IsBackground = true };
                    thread.Start();
                }
            }
            catch { }
        }

        private void Communicate()
        {
            while (true)
            {
                try
                {
                    if (plc.IsConnected)
                    {
                        IsConnected = plc.IsConnected;
                        datas = plc.ReadBytes(type, db, offset, len);
                        communicae.ShiftButtons = Bit.ToBitArray(datas.Skip(0).Take(2).ToArray());
                        communicae.StepTips = Bit.ToBitArray(datas.Skip(2).Take(8).ToArray());
                        communicae.Prompt = Bit.ToBitArray(datas.Skip(10).Take(4).ToArray());
                        communicae.Warning = Bit.ToBitArray(datas.Skip(14).Take(4).ToArray());
                        communicae.Alarm = Bit.ToBitArray(datas.Skip(18).Take(16).ToArray());
                        ReadTighten(datas.Skip(34).Take(34).ToArray());
                        communicae.ScanCode = datas[70];

                        mesData = plc.ReadBytes(type, mesDb, mesOffset, mesLen);
                        communicae.DevNum = Int.FromByteArray(mesData.Skip(0).Take(2).ToArray());
                        communicae.Result = Int.FromByteArray(mesData.Skip(2).Take(2).ToArray());
                        for (int i = 0; i < communicae.Devs.Length; i++)
                        {
                            communicae.Devs[i].Type = mesData[4 + i * 36];
                            communicae.Devs[i].Datas = Real.ToArray(mesData.Skip(6 + i * 36).Take(32).ToArray());
                            communicae.Devs[i].Result = Int.FromByteArray(mesData.Skip(38 + i * 36).Take(2).ToArray());
                        }
                        communicae.CollectFlag = Int.FromByteArray(mesData.Skip(580).Take(2).ToArray());
                    }
                    OnStatusChanged();
                    Thread.Sleep(100);
                }
                catch (Exception ex)
                {
                    //DbHelper.LogTip(ex.Message);
                }
            }
        }

        private void ReadTighten(byte[] data)
        {
            for (int i = 0; i < communicae.State.Length; i++)
            {
                communicae.State[i] = Bit.FromByte(data[0], (byte)i);
            }
            int shortByteLen = communicae.Shorts.Length * 2;
            int floatByteLen = communicae.Floats.Length * 4;
            byte[] temp = new byte[shortByteLen];
            Array.Copy(data, 2, temp, 0, shortByteLen);
            communicae.Shorts = Int.ToArray(temp);
            byte[] temp1 = new byte[floatByteLen];
            Array.Copy(data, 2 + shortByteLen, temp1, 0, floatByteLen);
            communicae.Floats = Real.ToArray(temp1);
        }

        public void WriteBool(int offset, int bitOffset, bool value)
        {
            if (IsConnected)
            {
                int carry = bitOffset / 8;
                int ret = bitOffset % 8;
                plc.WriteBit(type, db, offset + carry, ret, value);
            }
        }

        public void WriteData(int offset, float[] datas)
        {
            if (IsConnected)
            {
                byte[] bytes = Real.ToByteArray(datas);
                plc.WriteBytes(type, db, offset, bytes);
            }
        }
        public void WriteData(int offset, bool[] datas)
        {
            if (IsConnected)
            {
                byte[] bytes = BoolsToBytes(datas);
                plc.WriteBytes(type, db, offset, bytes);
            }
        }


        private byte[] BoolsToBytes(bool[] value)
        {
            int byteLength = value.Length % 8 == 0 ? value.Length / 8 : value.Length / 8 + 1;

            byte[] result = new byte[byteLength];

            for (int i = 0; i < result.Length; i++)
            {

                int total = value.Length < 8 * (i + 1) ? value.Length - 8 * i : 8;

                for (int j = 0; j < total; j++)
                {
                    result[i] = SetBitValue(result[i], j, value[8 * i + j]);
                }
            }
            return result;
        }

        private byte SetBitValue(byte src, int bit, bool value)
        {
            return value ? (byte)(src | (byte)Math.Pow(2, bit)) : (byte)(src & ~(byte)Math.Pow(2, bit));
        }

        public void WriteData(int offset, byte[] datas)
        {
            if (IsConnected)
            {
                plc.WriteBytes(type, db, offset, datas);
            }
        }

        public void WriteMesData(int offset, byte[] datas)
        {
            if (IsConnected)
            {
                plc.WriteBytes(type, mesDb, offset, datas);
            }
        }

        public void Write40Rfid(int offset, byte[] datas)
        {
            if (IsConnected)
            {
                plc.WriteBytes(type, 20003, offset, datas);
            }
        }
    }
}
